#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <limits.h>
#include "Debug.h"
#include "mxml.h"
#include "uthash.h"
#include "xmlhash_s.h"
#include "mxml-private.h"
#include "file_lock.h"
#include "xmlsock.h"
#include "loadflags.h"


typedef struct st_XML_Node
{
    char *pKey;
    char *pValue;
	UT_hash_handle hh;
}XML_Node;

// static XML_Node *g_xmlNodeHead = NULL;   // type 1
static XML_Node *g_xmlFileTree   = NULL;    // xmlfile <->  mxml.tree
static XML_Node *g_xmlFileModify = NULL;    // xmlfile <->  modify_flag (1 isModified.  0 filesaved)

// xml
int xmlhash_gets(const char * xmlfilename, const char * xpath, char * szValue, int nMaxLen, const char * szValueDefault);
int xmlhash_sets(const char * xmlfilename, const char * xpath, char * szValue, int nDelaySave);
int xmlhash_checkreload(const char * xmlfilename);
int xmlhash_loadfile(const char * xmlfilename);
int xmlhash_unloadfile(const char *xmlfilename);
int xmlhash_loadtree(mxml_node_t * tree);
int xmlhash_savefile(const char *xmlfilename, int nDelaySave);
int xmlhash_savefile_p(const char *xmlfilename, XML_Node* pArgHead, mxml_node_t *tree);
int xmlhash_copy2config(const char *xmlfilename);
int xmlhash_create_timer();

// hash
int xmlhash_hashadd(XML_Node **phead, const char *strKey, char *strVal);
int xmlhash_hashaddptr(XML_Node **phead, const char *strKey, void *ptr);
int xmlhash_hashset(XML_Node *phead, const char *strKey, char *strVal);
char *xmlhash_hashfind(XML_Node *phead, const char *strKey);
int  xmlhash_hashdel(XML_Node **phead, const char *strKey, int nFreeValue);
void xmlhash_hashdeleteAll(XML_Node **phead);

// tree
int  xmlhash_treeset(mxml_node_t * ptree, const char * pXpath, const char *strVal);
const char *xmlhash_treevalue(mxml_node_t * ptree, const char * pXpath);




mxml_type_t type_cb(mxml_node_t *node)
{
    const char *ptype = mxmlElementGetAttr(node, "type");
    if(NULL == ptype)
    {
        ptype = node->value.element.name;
    }

    if (!strcmp(ptype, "integer")) {
        return MXML_INTEGER;
    }else if (0 == strcmp(ptype, "opaque") || 0 == strcmp(ptype, "pre") ){
        return MXML_OPAQUE;
    }else if (0 == strcmp(ptype, "real")){
        return MXML_REAL;
    }else{
        return MXML_TEXT;
    }
}


const char * whitespace_cb(mxml_node_t *node,int where)
{
    mxml_node_t *parent = 0;
    int level = 0;
    const char *name = 0;
    static const char *tabs = "\t\t\t\t\t\t\t\t";
    name = node->value.element.name;

    if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
            !strcmp(name, "pre") || !strcmp(name, "p") ||
            !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
            !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
    {
        if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
            return ("\n");
    }
    else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
    {
        return ("\n");
    }
    else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
    {
        if (where == MXML_WS_BEFORE_OPEN)
            return ("\t");
        else if (where == MXML_WS_AFTER_CLOSE)
            return ("\n");
    }
    else if (!strncmp(name, "?xml", 4))
    {
        if (where == MXML_WS_AFTER_OPEN)
            return ("\n");
        else
            return (NULL);
    }
    else if (where == MXML_WS_BEFORE_OPEN ||
             ((!strcmp(name, "choice") || !strcmp(name, "option")) &&
              where == MXML_WS_BEFORE_CLOSE))
    {
        for (level = -1, parent = node->parent;
                parent;
                level ++, parent = parent->parent);

        if (level > 8)
            level = 8;
        else if (level < 0)
            level = 0;

        return (tabs + 8 - level);
    }
    else if (where == MXML_WS_AFTER_CLOSE ||
             ((!strcmp(name, "group") || !strcmp(name, "option") ||
               !strcmp(name, "choice")) &&
              where == MXML_WS_AFTER_OPEN))
        return ("\n");
    else if (where == MXML_WS_AFTER_OPEN && !node->child)
        return ("\n");

    return (NULL);
}


int xmlhash_loadtree(mxml_node_t *tree)
{
    if (NULL == tree)
    {
        Safe_Printf("tree=null");
        return -1;
    }
    mxml_node_t *top = tree;
    char *szTag = NULL;      // strdup("sipsetting");
    mxml_node_t *node = mxmlFindElement(top, top, szTag, NULL, NULL, MXML_DESCEND_FIRST);
    if(NULL == node)
    {
        Safe_Printf("node=null.[%s]", szTag);
        return -1;
    }
    mxml_node_t *argNode = mxmlGetFirstChild(node);
    //argNode = mxmlGetNextSibling(argNode);		//<!--SIP User Account 1-->
    int nGetArgNum = 0;
    for(nGetArgNum = 0; NULL != argNode; argNode = mxmlGetNextSibling(argNode))
    {
        const char *szName = mxmlGetElement(argNode);
        const char *szText = mxmlGetText(argNode,NULL);
        if(NULL == szName || NULL == szText)
        {
            // Safe_Printf("error node,%s,%s", szName,szText);
            continue ;
        }
//        Safe_Printf("num=%d, %d, %s, %s", nGetArgNum, argNode->type, szName, szText);
        XML_Node *pnode = (XML_Node*)malloc(sizeof(XML_Node));
        bzero(pnode, sizeof(XML_Node));
//        snprintf(arg1->szName, sizeof(arg1->szName), "%s%s", "sipsetting/M_SIP/", szName);
//        snprintf(arg1->szText, sizeof(arg1->szText), "%s",  szText);
//        Safe_Printf("%s = %s", szName, szText);
        nGetArgNum++;
    }
    return nGetArgNum;
}


// print node 
void xmlhash_hashprint(XML_Node *phead)
{
    if(NULL == phead)
    {
        Safe_Printf("phead=null");
        return ;
    }
    Safe_Printf("print hash..................................");
    XML_Node *curNode = 0, *tmp = 0;
    HASH_ITER(hh, phead, curNode, tmp)
    {
        Safe_Printf("node:%s,%p,[%s]",curNode->pKey, curNode->pValue, curNode->pValue);
    }
}

void xmlhash_hashdeleteAll(XML_Node **phead)
{
    if(NULL == phead || NULL == *phead)
    {
        return ;
    }
    XML_Node *curNode = 0, *tmp = 0;
    HASH_ITER(hh, *phead, curNode, tmp)
    {
        HASH_DEL(*phead, curNode);
        free(curNode);
    }
}


// nvram_get_func2("phonesetting/M_KEY/Key_type1", tmp_key_type1, DIR_PHONESETTING);
// nvram_get_func2("sipsetting/M_DTF/DTMF", tmp,    "sipsettings.xml");
int xmlhash_loadfile(const char * xmlfilename)
{
    if(NULL == xmlfilename || strlen(xmlfilename) <= 0)
    {
        Safe_Printf("path is null.%s", xmlfilename);
        return -1;
    }
    char *sVal = xmlhash_hashfind(g_xmlFileModify, xmlfilename);
    if (NULL != sVal)
    {
        Safe_Printf("exist %s", xmlfilename);
        return 0;
    }
    // set xml file loaded, isModified = 0
    xmlhash_hashadd(&g_xmlFileModify, xmlfilename, "0");
    Safe_Printf("hashadd file %s, xmlfilehead=%p", xmlfilename, g_xmlFileModify);

    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(ptree)
    {
        Safe_Printf("exist tree %s", xmlfilename);
        return 0;
    }
    // xml load start
    int nlocfd = file_lock(xmlfilename);
    FILE *fp = NULL;
    int ret = 0;
    do
    {
        fp = fopen(xmlfilename, "r");
        if(NULL == fp)
        {
            Safe_Printf("open error. %s", xmlfilename);
            break;
        }
        ptree = mxmlLoadFile(NULL, fp, type_cb);
        fclose(fp); fp = NULL;
        Safe_Printf("load xml start...... %s",xmlfilename);
        ret = xmlhash_loadtree(ptree);
        Safe_Printf("load xml finished...... %s, ptree=%p",xmlfilename, ptree);
        xmlhash_hashaddptr(&g_xmlFileTree, xmlfilename, (void *)ptree);
    }while(0);
    if(fp)
        fclose(fp);
    file_unlock(nlocfd);
    loadflags_set(xmlfilename);
    return ret;
}

int xmlhash_unloadfile(const char *xmlfilename)
{
    if(NULL == xmlfilename || strlen(xmlfilename) <= 0)
    {
        Safe_Printf("xml=null,%s", xmlfilename);
        return -1;
    }
    char *pMod = xmlhash_hashfind(g_xmlFileModify, xmlfilename);
    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(NULL == pMod || NULL == ptree)
    {
        Safe_Printf("not exist xml=%s", xmlfilename);
    }
    xmlhash_hashdel(&g_xmlFileModify, xmlfilename, 1);
    xmlhash_hashdel(&g_xmlFileTree,   xmlfilename, 0);   // free must use mxmlDelete
    mxmlDelete(ptree);
    Safe_Printf("unloadfile:%s succ", xmlfilename);
    return 0;
}


int xmlhash_hashadd(XML_Node **pphead, const char *strKey, char *strVal)
{
    if(NULL == pphead)
        return -1;
    XML_Node *pnode = (XML_Node *)malloc(sizeof(XML_Node));
    memset(pnode, 0, sizeof(XML_Node));
    pnode->pKey = strdup(strKey);
    pnode->pValue = strdup(strVal);
    HASH_ADD_STR(*pphead, pKey, pnode);
    return 0;
}

int xmlhash_hashaddptr(XML_Node **pphead, const char *strKey, void *strPtr)
{
    if(NULL == pphead)
        return -1;
    XML_Node *pnode = (XML_Node *)malloc(sizeof(XML_Node));
    memset(pnode, 0, sizeof(XML_Node));
    pnode->pKey = strdup(strKey);
    pnode->pValue = strPtr;
    HASH_ADD_STR(*pphead, pKey, pnode);
    return 0;
}


int xmlhash_hashset(XML_Node *phead, const char *strKey, char *strVal)
{
    if(NULL == phead || NULL ==  strKey || NULL == strVal)
    {
        Safe_Printf("error p.null.%p,%p,%p", phead, strKey, strVal);
        return -1;
    }
    XML_Node *out = NULL;
    HASH_FIND_STR(phead, strKey, out);
    if (0 == out)
    {
        Safe_Printf("error.not found [%s]", strKey);
        return -1;
    }
    if(out->pValue)
        free(out->pValue);
    out->pValue = strdup(strVal);
    return 0;
}


char *xmlhash_hashfind(XML_Node *phead, const char *strKey)
{
    if(NULL == phead)
        return NULL;
    XML_Node *out = NULL;
    HASH_FIND_STR(phead, strKey, out);
    if(NULL == out)
    {
        return NULL;
    }
    return out->pValue;
}

int  xmlhash_hashdel(XML_Node **phead, const char *strKey, int nFreeValue)
{
    if(NULL == phead || NULL == *phead)
        return -1;
    XML_Node *out = NULL;
    HASH_FIND_STR(*phead, strKey, out);
    if(NULL == out)
    {
        return -1;
    }
    HASH_DEL(*phead, out);
    if(out->pKey) free(out->pKey);
    if(nFreeValue > 0)
    {
        if(out->pValue) 
            free(out->pValue);
    }
    free(out);
    return 0;
}


const char *xmlhash_treevalue(mxml_node_t *ptree, const char *pXpath)
{
    if(NULL == ptree)
        return NULL;
    mxml_node_t *node = mxmlFindPath(ptree, pXpath);
    if(NULL == node)
    {
        Safe_Printf("error not find node=%s", pXpath);
        return NULL;
    }
    const char *ptext = mxmlGetText(node, NULL);
//    Safe_Printf("node=%s val=[%s]", pXpath, ptext);
    return ptext;
}

int  xmlhash_treeset(mxml_node_t * ptree, const char * pXpath, const char *strVal)
{
    if(NULL == ptree)
        return -1;
    mxml_node_t *node = mxmlFindPath(ptree, pXpath);
    if(NULL == node)
    {
        Safe_Printf("error not find node=%s", pXpath);
        return -1;
    }
    if (strlen(strVal) > 0)
    {
        mxmlSetText(node, 0, strVal);
        Safe_Printf("node=%s set value=[%s]", pXpath, strVal);
    }
    else
    {
        mxmlSetText(node, 0, "");
        Safe_Printf("node=%s set value=[null]", pXpath);
    }
    return 0;
}


int xmlhash_init(int nType)
{
//    xmlhash_loadfile(CONF_PHONESETTING);
//    xmlhash_loadfile(CONF_SIPSETTING);
//    xmlhash_loadfile(CONF_NETWORKSET);
    xmlhash_create_timer();
    return 0;
}


int xmlhash_gets(const char * xmlfilename, const char * pXpath, char * szValue, int nMaxLen, const char * szValueDefault)
{
    xmlhash_checkreload(xmlfilename);
    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(NULL == ptree)
    {
        int ret = xmlhash_loadfile(xmlfilename);
        if(ret < 0)
        {
            return -1;
        }
        ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
        if(NULL == ptree)
        {
            return -1;
        }
    }
    const char *szVal = xmlhash_treevalue(ptree, pXpath);  //   type 2
    if (0 == szVal)
    {
        Safe_Printf("error.not found %s[%s]",xmlfilename, pXpath);
        if(NULL != szValueDefault)
        {
            snprintf(szValue, nMaxLen, "%s", szValueDefault);
        }
    } else {
        snprintf(szValue, nMaxLen, "%s", szVal);
    }
//    Safe_Printf("%s[%s]=%s",xmlfilename, pXpath, szValue);
    return 0;
}

int xmlhash_sets(const char * xmlfilename, const char * pXpath, char * szValue, int nDelaySave)
{
    xmlhash_checkreload(xmlfilename);
    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(NULL == ptree)
    {
        Safe_Printf("error not load xml=%s", xmlfilename);
        return -1;
    }
    int ret = -1;
    ret = xmlhash_treeset(ptree, pXpath, szValue);     // type 2
    xmlhash_hashset(g_xmlFileModify, xmlfilename, "1");
    xmlhash_savefile(xmlfilename, nDelaySave);
    return ret;
}

int xmlhash_getn(const char * xmlfilename, const char * xpath, int *pValue, int *nValueDefault)
{
    char szTmp[16] = {0};
    char szNumber[16] = {0};
    char *szValueDefault = NULL;
    if(nValueDefault)
    {
        snprintf(szNumber, 16, "%d", *nValueDefault);
        szValueDefault = szNumber;
    }
    int ret = xmlhash_gets(xmlfilename, xpath, szTmp, sizearray(szTmp), szValueDefault);
    int value = atoi(szTmp);
    memcpy(pValue, &value, sizeof(value));
    return ret;
}

int xmlhash_setn(const char * xmlfilename, const char * xpath, int nValue, int nDelaySave)
{
    char szTmp[16] = {0};
    snprintf(szTmp, 16, "%d", nValue);
    return xmlhash_sets(xmlfilename, xpath, szTmp, nDelaySave);
}

int xmlhash_getattr(const char *xmlfile, const char *nodepath, const char *szAttrName, char *szAttrValue, int nLen)
{
    return 0;
}

int xmlhash_get_attr_dialrule(const char* xmlfilename, const char* xpath, Dialrule_st2* pst_dialrule, int nMaxLen)
{
    mxml_node_t *top = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    mxml_node_t *node, *tmpnode;
    const char  *text = NULL;
    int count = 0;
	
	if(NULL == top)
	{
		Safe_Printf("error not load xml=%s", xmlfilename);
		return -1;
	}

    node = mxmlFindPath(top, xpath);
    if(NULL == node)
    {
        Safe_Printf("error not find node=%s", xpath);
        return -1;
    }

    tmpnode = mxmlGetFirstChild(mxmlGetParent(node));
    while (tmpnode != NULL && count < nMaxLen)
    {
        if ((text = mxmlElementGetAttr(tmpnode, "rule")) != NULL)
        {
            memset(pst_dialrule[count].dialrule,0,32);
            strncpy(pst_dialrule[count].dialrule, text, strlen(text));
			
			if ((text = mxmlElementGetAttr(tmpnode, "account")) != NULL && text[0] != 0)
			{
				memset(pst_dialrule[count].account,0,64);
				strncpy(pst_dialrule[count].account, text, strlen(text));
			}
            else
            {
                memset(pst_dialrule[count].account,0,64);
                strcpy(pst_dialrule[count].account,"all");
            }  

            if((text = mxmlElementGetAttr(tmpnode,"index")) != NULL)
            {
                memset(pst_dialrule[count].index,0,32);
                strncpy(pst_dialrule[count].index, text ,strlen(text));
            }
            count++;
        }
      
		tmpnode = mxmlGetNextSibling(tmpnode);
    }
    
    return 0;
}

int xmlhash_get_attr_limitNumber(const char* xmlfilename, const char* xpath, Limitnum_st2* pst_limitnum, int nMaxLen)
{
    mxml_node_t *top = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    mxml_node_t *node, *tmpnode;
    const char  *text = NULL;
    int count = 0;

	if(NULL == top)
	{
		Safe_Printf("error not load xml=%s", xmlfilename);
		return -1;
	}

    node = mxmlFindPath(top, xpath);
    if(NULL == node)
    {
        Safe_Printf("error not find node=%s", xpath);
        return -1;
    }

    tmpnode = mxmlGetFirstChild(mxmlGetParent(node));
    while (tmpnode != NULL && count < nMaxLen)
    {
        if ((text = mxmlElementGetAttr(tmpnode, "limit_num")) != NULL)
        {
            memset(pst_limitnum[count].number,0,64);
            strncpy(pst_limitnum[count].number, text, strlen(text));
            
            if ((text = mxmlElementGetAttr(tmpnode, "account")) != NULL && text[0] != 0)
            {
                memset(pst_limitnum[count].account,0,32);
                strncpy(pst_limitnum[count].account, text, strlen(text));
            }
            else
            {
                memset(pst_limitnum[count].account,0,32);
                strcpy(pst_limitnum[count].account,"all");
            }  

            if((text = mxmlElementGetAttr(tmpnode,"index")) != NULL)
            {
                memset(pst_limitnum[count].index,0,32);
                strncpy(pst_limitnum[count].index, text ,strlen(text));
            }
            count++;
        }
      
        tmpnode = mxmlGetNextSibling(tmpnode);
    }
    
    return 0;
}

int xmlhash_setattr(const char *xmlfile, const char *nodepath, const char *szAttrName, char *szAttrValue, int nSaveDelay)
{
    return 0;
}

int xmlhash_savefile(const char *xmlfilename, int nDelaySave)
{
    char *sModi = xmlhash_hashfind(g_xmlFileModify, xmlfilename);
    if(NULL == sModi)
    {
        Safe_Printf("error no exist %s", xmlfilename);
        return 0;
    }
    //Safe_Printf("xml %s, mod = %s", xmlfilename, sModi);
    if(0 == strcmp(sModi, "0"))
    {
        //Safe_Printf("xml no modify. skip save.%s", xmlfilename);
        return 0;
    }
    if(nDelaySave)
    {
        Safe_Printf("xml save mode = delay");
        return 0;
    }
    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(NULL == ptree)
    {
        Safe_Printf("error xml not load. save failed.%s", xmlfilename);
        return -1;
    }
    int ret = xmlhash_savefile_p(xmlfilename, g_xmlFileModify, ptree);
    if(ret < 0)
    {
        Safe_Printf("error save xmlfile=%s", xmlfilename);
        return ret;
    }
    loadflags_clearAllProc(xmlfilename);
    loadflags_set(xmlfilename);
    xmlhash_copy2config(xmlfilename);
    xmlhash_hashset(g_xmlFileModify, xmlfilename, "0");
    return ret;
}

int xmlhash_copy2config(const char *xmlfilename)
{
    // cp /tmp/sipsettings.xml /mnt/config
    if(strstr(xmlfilename, "/tmp/") == 0)
    {
        Safe_Printf("error not /tmp/ xml, [%s], skip copy", xmlfilename);
        return -1;
    }
    char szCmd[64] = {0};
    snprintf(szCmd, 64, "cp -f %s /mnt/config", xmlfilename);
    Safe_Printf("%s", szCmd);
    system(szCmd);
    return 0;
}

int xmlhash_savefile_p(const char *xmlfilename, XML_Node* phead, mxml_node_t *ptree)
{
    if(NULL == xmlfilename || NULL == phead || NULL == ptree)
    {
        Safe_Printf("error savefile phead=%p,%p,%p", xmlfilename, phead, ptree);
        return -1;
    }
    Safe_Printf("xmlfile save begin:%s", xmlfilename);
    int nlocfd = file_lock(xmlfilename);
    FILE *fp = NULL;
    do{
       fp = fopen(xmlfilename, "w");
       if(NULL == fp)
       {
           Safe_Printf("open error.path=%s", xmlfilename);
           break;
       }
       mxmlSaveFile(ptree, fp, whitespace_cb);
    }while(0);
    if(fp)
        fclose(fp);
    file_unlock(nlocfd);
    Safe_Printf("xmlfile save finished:%s", xmlfilename);
	return 0;
}

int xmlhash_savefiles()
{
    XML_Node *el = 0, *tmp = 0;
    HASH_ITER(hh, g_xmlFileModify, el, tmp)
    {
        xmlhash_savefile(el->pKey, XML_SAVE_NOW);
    }
    return 0;
}

int xmlhash_replace(const char * szXmlfile_Old, const char * xmlfile_tmp, const char * xmlfile_config)
{
    
    return 0;
}


int xmlhash_reload()
{
    XML_Node *curNode = 0, *tmp = 0;
    HASH_ITER(hh, g_xmlFileTree, curNode, tmp)
    {
        xmlhash_unloadfile(curNode->pKey);
    }
    return 0;
}

int xmlhash_checkreload(const char *xmlfilename)
{
    int nflag = loadflags_get(xmlfilename);
    if(nflag <= 0)
    {
        Safe_Printf("warning xml:%s,flag:%d", xmlfilename, nflag);
        xmlhash_unloadfile(xmlfilename);
    }
    mxml_node_t *ptree = (mxml_node_t *)xmlhash_hashfind(g_xmlFileTree, xmlfilename);
    if(NULL == ptree)
    {
        xmlhash_loadfile(xmlfilename);
    }
    return nflag;
}


// save timer
void *thread_func(void *param)
{
    while(1)
    {
        sleep(2);
//        Safe_Printf("save thread func");
        xmlhash_savefiles();
    }
}


int xmlhash_create_timer()
{
    pthread_attr_t attr1542;
    pthread_attr_init(&attr1542);
    pthread_attr_setstacksize(&attr1542, PTHREAD_STACK_MIN) ; // PTHREAD_STACK_MIN);
    pthread_t thread;
    extern int errno;
    int ret = pthread_create(&thread, &attr1542, thread_func, NULL);
    pthread_attr_destroy(&attr1542);
    if(0 != ret)
    {
        Safe_Printf("error pthread create ret=%d,%s", ret, strerror(errno));
        exit(0);
    }
    return 0;
}


